#include "test.h"
#include "test_matrix.h"

Mat2 transpose(const Mat2 m)
{
	return m.transpose();
}

Mat3 transpose(const Mat3 m)
{
	return m.transpose();
}

Mat4 transpose(const Mat4 m)
{
	return m.transpose();
}

float sumInPowerOfTwo(int limit){
	float res=0.0;
	float i=0.0;
	while(i<limit){
		i+=1.0;
		res+=(i*i);
	}
	return res;
}

bool test_mat2()
{
	int range=2;
	Vec2 vec1(1.0, 2.0);
	Vec2 vec2(vec1);
	Mat2 matByCol(vec1, vec2);
	Mat2 matByRow(1.0, 2.0, 1.0, 2.0);
	Mat2 mat1;
	Mat2 mat2(2.0);
	Mat2 mat3(matByRow);
	Mat2 mat4(matByCol);
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			if(i==j){
				ASSERT_EQUALS(mat1[i][j],1.0);
			}
			else{
				ASSERT_EQUALS(mat1[i][j],0.0);
			}
			if(i==j){
				ASSERT_EQUALS(mat2[i][j],2.0);
			}
			else{
				ASSERT_EQUALS(mat2[i][j],0.0);
			}
			ASSERT_EQUALS(matByRow[i][j],1.0+i);
			ASSERT_EQUALS(matByCol[i][j],1.0+j);
			ASSERT_EQUALS(mat3[i][j],1.0+i);
			ASSERT_EQUALS(mat4[i][j],1.0+j);
		}
	}
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){	
			ASSERT_EQUALS((matByRow+matByCol)[i][j],2.0+i+j);
			ASSERT_EQUALS((matByCol+matByRow)[i][j],2.0+i+j);
			ASSERT_EQUALS((matByRow-matByCol)[i][j],i-j);
			ASSERT_EQUALS((matByCol-matByRow)[i][j],j-i);
			ASSERT_EQUALS((matByRow*((float)i+(float)j))[i][j],((float)i+1.0)*((float)i+(float)j));
			ASSERT_EQUALS((((float)i+(float)j)*matByRow)[i][j],((float)i+1.0)*((float)i+(float)j));
			ASSERT_EQUALS((matByCol*((float)i+(float)j))[i][j],((float)j+1.0)*((float)i+(float)j));
			ASSERT_EQUALS((((float)i+(float)j)*matByCol)[i][j],((float)j+1.0f)*((float)i+(float)j));
			ASSERT_DOUBLE_EQAULS((matByRow/((float)i+(float)j+1.0f))[i][j],(((float)i+1.0f)/((float)i+(float)j+1.0f)));
			ASSERT_DOUBLE_EQAULS((matByCol/((float)i+(float)j+1.0f))[i][j],(((float)j+1.0f)/((float)i+(float)j+1.0f)));
		}
	}
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS((mat1*matByCol)[i][j],j+1.0);
			ASSERT_EQUALS((mat1*matByRow)[i][j],i+1.0);
			ASSERT_EQUALS((mat2*matByCol)[i][j],2.0*(j+1.0));
			ASSERT_EQUALS((mat2*matByRow)[i][j],2.0*(i+1.0));
			ASSERT_EQUALS((matByRow*matByCol)[i][j],((i+1)*(j+1))*range);
			ASSERT_EQUALS((matByCol*matByRow)[i][j],sumInPowerOfTwo(range));
		}
	}

	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS((transpose(mat1))[i][j],mat1[i][j]);
			ASSERT_EQUALS((transpose(mat2))[i][j],mat2[i][j]);
			ASSERT_EQUALS((transpose(matByCol))[i][j],transpose((transpose(matByCol)))[i][j]);
			ASSERT_EQUALS((transpose(matByRow))[i][j],transpose((transpose(matByRow)))[i][j]);
			ASSERT_EQUALS((transpose(matByCol))[i][j],matByRow[j][i]);
			ASSERT_EQUALS((transpose(matByRow))[i][j],matByCol[j][i]);
		}
	}

	mat4+=matByRow;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],2.0+i+j);
		}
	}

	mat4-=matByRow;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],1.0+j);
		}
	}

	mat4*=1;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],1.0+j);
		}
	}

	mat4*=3;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			mat4/=1;
			ASSERT_EQUALS(mat4[i][j],3*(1.0+j));
		}
	}

	mat4/=3;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],1.0+j);
		}
	}
	mat4*=matByCol;
	mat3*=matByRow;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],(matByCol*matByCol)[i][j]);
			ASSERT_EQUALS(mat3[i][j],(matByRow*matByRow)[i][j]);
		}
	}

	float vec2Sum=0;
	for(int i=0; i<range; ++i){
		vec2Sum+=vec2[i];
	}
	for(int i=0; i<range; ++i){
			ASSERT_EQUALS((matByRow*vec2)[i],(i+1)*vec2Sum);
	}

	return true;
}

bool test_mat3()
{
	int range=3;
	Vec3 vec1(1.0, 2.0, 3.0);
	Vec3 vec2(vec1);
	Mat3 matByRow(1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0);
	Mat3 matByCol(vec1, vec1, vec1);
	Mat3 mat1;
	Mat3 mat2(2.0);
	Mat3 mat3(matByRow);
	Mat3 mat4(matByCol);

	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			if(i==j){
				ASSERT_EQUALS(mat1[i][j],1.0);
			}
			else{
				ASSERT_EQUALS(mat1[i][j],0.0);
			}
			if(i==j){
				ASSERT_EQUALS(mat2[i][j],2.0);
			}
			else{
				ASSERT_EQUALS(mat2[i][j],0.0);
			}
			ASSERT_EQUALS(matByRow[i][j],1.0+i);
			ASSERT_EQUALS(matByCol[i][j],1.0+j);
			ASSERT_EQUALS(mat3[i][j],1.0+i);
			ASSERT_EQUALS(mat4[i][j],1.0+j);
		}
	}
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){	
			ASSERT_EQUALS((matByRow+matByCol)[i][j],2.0+i+j);
			ASSERT_EQUALS((matByCol+matByRow)[i][j],2.0+i+j);
			ASSERT_EQUALS((matByRow-matByCol)[i][j],i-j);
			ASSERT_EQUALS((matByCol-matByRow)[i][j],j-i);
			ASSERT_EQUALS((matByRow*((float)i+(float)j))[i][j],((float)i+1.0)*((float)i+(float)j));
			ASSERT_EQUALS((((float)i+(float)j)*matByRow)[i][j],((float)i+1.0)*((float)i+(float)j));
			ASSERT_EQUALS((matByCol*((float)i+(float)j))[i][j],((float)j+1.0)*((float)i+(float)j));
			ASSERT_EQUALS((((float)i+(float)j)*matByCol)[i][j],((float)j+1.0)*((float)i+(float)j));
			ASSERT_DOUBLE_EQAULS((matByRow/((float)i+(float)j+1.0f))[i][j],(((float)i+1.0f)/((float)i+(float)j+1.0f)));
			ASSERT_DOUBLE_EQAULS((matByCol/((float)i+(float)j+1.0f))[i][j],(((float)j+1.0f)/((float)i+(float)j+1.0f)));
		}
	}
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS((mat1*matByCol)[i][j],j+1.0);
			ASSERT_EQUALS((mat1*matByRow)[i][j],i+1.0);
			ASSERT_EQUALS((mat2*matByCol)[i][j],2.0*(j+1.0));
			ASSERT_EQUALS((mat2*matByRow)[i][j],2.0*(i+1.0));
			ASSERT_EQUALS((matByRow*matByCol)[i][j],((i+1)*(j+1))*range);
			ASSERT_EQUALS((matByCol*matByRow)[i][j],sumInPowerOfTwo(range));
		}
	}
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS((transpose(mat1))[i][j],mat1[i][j]);
			ASSERT_EQUALS((transpose(mat2))[i][j],mat2[i][j]);
			ASSERT_EQUALS((transpose(matByCol))[i][j],transpose((transpose(matByCol)))[i][j]);
			ASSERT_EQUALS((transpose(matByRow))[i][j],transpose((transpose(matByRow)))[i][j]);
			ASSERT_EQUALS((transpose(matByCol))[i][j],matByRow[j][i]);
			ASSERT_EQUALS((transpose(matByRow))[i][j],matByCol[j][i]);
		}
	}

	mat4+=matByRow;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],2.0+i+j);
		}
	}

	mat4-=matByRow;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],1.0+j);
		}
	}

	mat4*=1;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],1.0+j);
		}
	}

	mat4*=3;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			mat4/=1;
			ASSERT_EQUALS(mat4[i][j],3*(1.0+j));
		}
	}

	mat4/=3;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],1.0+j);
		}
	}
	mat4*=matByCol;
	mat3*=matByRow;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],(matByCol*matByCol)[i][j]);
			ASSERT_EQUALS(mat3[i][j],(matByRow*matByRow)[i][j]);
		}
	}

	float vec2Sum=0;
	for(int i=0; i<range; ++i){
		vec2Sum+=vec2[i];
	}
	for(int i=0; i<range; ++i){
			ASSERT_EQUALS((matByRow*vec2)[i],(i+1)*vec2Sum);
	}

	return true;

	return true;
}

bool test_mat4()
{
	int range=4;
	Vec4 vec1(1.0, 2.0, 3.0, 4.0);
	Vec4 vec2(vec1);
	Mat4 matByRow(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0);
	Mat4 matByCol(vec1, vec1, vec1, vec1);
	Mat4 mat1;
	Mat4 mat2(2.0);
	Mat4 mat3(matByRow);
	Mat4 mat4(matByCol);

	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			if(i==j){
				ASSERT_EQUALS(mat1[i][j],1.0);
			}
			else{
				ASSERT_EQUALS(mat1[i][j],0.0);
			}

			if(i==j){
				ASSERT_EQUALS(mat2[i][j],2.0);
			}
			else{
				ASSERT_EQUALS(mat2[i][j],0.0);
			}

			ASSERT_EQUALS(matByRow[i][j],1.0+i);
			ASSERT_EQUALS(matByCol[i][j],1.0+j);
			ASSERT_EQUALS(mat3[i][j],1.0+i);
			ASSERT_EQUALS(mat4[i][j],1.0+j);
		}
	}

	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){	
			ASSERT_EQUALS((matByRow+matByCol)[i][j],2.0+i+j);
			ASSERT_EQUALS((matByCol+matByRow)[i][j],2.0+i+j);
			ASSERT_EQUALS((matByRow-matByCol)[i][j],i-j);
			ASSERT_EQUALS((matByCol-matByRow)[i][j],j-i);
			ASSERT_EQUALS((matByRow*((float)i+(float)j))[i][j],((float)i+1.0f)*((float)i+(float)j));
			ASSERT_EQUALS((((float)i+(float)j)*matByRow)[i][j],((float)i+1.0f)*((float)i+(float)j));
			ASSERT_EQUALS((matByCol*((float)i+(float)j))[i][j],((float)j+1.0f)*((float)i+(float)j));
			ASSERT_EQUALS((((float)i+(float)j)*matByCol)[i][j],((float)j+1.0f)*((float)i+(float)j));
			ASSERT_DOUBLE_EQAULS((matByRow/((float)i+(float)j+1.0f))[i][j],(((float)i+1.0f)/((float)i+(float)j+1.0f)));
			ASSERT_DOUBLE_EQAULS((matByCol/((float)i+(float)j+1.0f))[i][j],(((float)j+1.0f)/((float)i+(float)j+1.0f)));
		}
	}

	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS((mat1*matByCol)[i][j],j+1.0);
			ASSERT_EQUALS((mat1*matByRow)[i][j],i+1.0);
			ASSERT_EQUALS((mat2*matByCol)[i][j],2.0*(j+1.0));
			ASSERT_EQUALS((mat2*matByRow)[i][j],2.0*(i+1.0));
			ASSERT_EQUALS((matByRow*matByCol)[i][j],((i+1)*(j+1))*range);
			ASSERT_EQUALS((matByCol*matByRow)[i][j],sumInPowerOfTwo(range));
		}
	}

	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS((transpose(mat1))[i][j],mat1[i][j]);
			ASSERT_EQUALS((transpose(mat2))[i][j],mat2[i][j]);
			ASSERT_EQUALS((transpose(matByCol))[i][j],transpose((transpose(matByCol)))[i][j]);
			ASSERT_EQUALS((transpose(matByRow))[i][j],transpose((transpose(matByRow)))[i][j]);
			ASSERT_EQUALS((transpose(matByCol))[i][j],matByRow[j][i]);
			ASSERT_EQUALS((transpose(matByRow))[i][j],matByCol[j][i]);
		}
	}

	mat4+=matByRow;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],2.0+i+j);
		}
	}

	mat4-=matByRow;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],1.0+j);
		}
	}

	mat4*=1;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],1.0+j);
		}
	}

	mat4*=3;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			mat4/=1;
			ASSERT_EQUALS(mat4[i][j],3*(1.0+j));
		}
	}

	mat4/=3;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],1.0+j);
		}
	}
	mat4*=matByCol;
	mat3*=matByRow;
	for(int i=0; i<range; ++i){
		for(int j=0; j<range; ++j){
			ASSERT_EQUALS(mat4[i][j],(matByCol*matByCol)[i][j]);
			ASSERT_EQUALS(mat3[i][j],(matByRow*matByRow)[i][j]);
		}
	}

	float vec2Sum=0;
	for(int i=0; i<range; ++i){
		vec2Sum+=vec2[i];
	}
	for(int i=0; i<range; ++i){
			ASSERT_EQUALS((matByRow*vec2)[i],(i+1)*vec2Sum);
	}

	Mat4 mat5 = Mat4(
		Vec4(4.0f, 5.0f, 1.0f, 9.0f),
		Vec4(2.0f, 4.0f, 5.0f, 4.0f),
		Vec4(4.0f, 6.0f, 5.0f, 3.0f),
		Vec4(1.0f, 3.0f, 3.0f, 3.0f)
	);

	Mat4 mat6 = Mat4(
		Vec4(1.0f, 5.0f, 3.0f, 9.0f),
		Vec4(2.0f, 3.0f, 4.0f, 6.0f),
		Vec4(2.0f, 9.0f, 4.0f, 3.0f),
		Vec4(2.0f, 3.0f, 9.0f, 4.0f)
	);

	Mat4 mat7(
		Vec4(34.0f,  71.0f, 117.0f, 105.0f),
		Vec4(28.0f,  79.0f,  78.0f,  73.0f),
		Vec4(32.0f,  92.0f,  83.0f,  99.0f),
		Vec4(19.0f,  50.0f,  54.0f,  48.0f)
	);

	Mat4 mat8 = mat5*mat6;

	for (int i = 0; i < 4; ++i)
		for (int j = 0; j < 4; ++j)
			ASSERT_EQUALS(mat8[i][j], mat7[i][j]);

	return true;
}